tl;dr
This notebook determines the model framework utilized for this PRD. It utilizes the results from the hyperparameter tuning step, and trains the optimal model for each algorithm on the v67 dataset. The resultant predictions of these optimal models are compared to a v68 validation dataset. The framework used to generate the highest performant model (e.g., features, hyperparameters) will be used in online model training for the subsetting of Beta in production use.
Features
Feature sets:
- Only performance metrics.
- Performance metrics and the highest found by Boruta with original dataset.
- Performance metrics and the highest found by Boruta with equal labels dataset.
- Performance metrics and all covariates.
- Only the highest covariates found by Boruta (excluding performance).
- Only the highest covariates found by Boruta (equal labels, excluding performance).
- Utilize all covariates (excluding performance).
Validation Bootstraps
30 bootstrap replicates of v68 Release will be utilized to validate the models. There are 257697 profiles utilized for this purpose.
[1] "Loading previously created bootstraps from file"
Oversampling
The entire v67 dataset will be utilized for training. However, most of the matching algorithms are more performant with higher ratios of Beta to Release. Therefore, various levels of oversampled datasets are created for training. There are 70345 profiles utilized for this purpose.
[1] "Loading previously created oversampling training sets from file"
Model Training
Two matching methods were ultimately tested, as CEM and subclassing were producing extremely poor results.
- nearest-neighbors
- genetic matching
For all models the following diagnostics are reported:
- mean, median score against the 30 validation replicates
- Number of matched Beta samples
- QQ and ridge plots of original and matched Beta samples
Nearest-Neighbors, Malahanobis
Hyperparameters: hyperparameter_tuning_nn_malahanobis.Rmd
Feature selection and hyperparameter tuning for the nearest-neighbors models were all trained using the MatchIt library. These models are trained using the full, relevantly oversampled, training dataset.
FS 1
caliper = 0.20
calcloset = TRUE
ratio = 2
replace = FALSE
- 4x oversampling
mean median
0.001665983 0.001671272
Number of Beta samples: 29814

FS 3
caliper = 3.00
calcloset = TRUE
ratio = 1
replace = TRUE
- 16x oversampling
mean median
0.004966338 0.004966342
Number of Beta samples: 3259

FS 5
caliper = 0
calcloset = TRUE
ratio = 1
replace = FALSE
- 8x oversampling
mean median
0.004599767 0.004598880
Number of Beta samples: 6208

Nearest-Neighbors Logit, Linear
Hyperparameters: hyperparameter_tuning_nn_logit_linear.Rmd
FS 1
caliper = 0.4
calcloset = TRUE
ratio = 3
replace = FALSE
- 4x oversampling
- Interactions across covariates
mean median
0.001789146 0.001795468
Number of Beta samples: 44721

FS 5
caliper = 0
calcloset = FALSE
ratio = 1
replace = FALSE
- 8x oversampling
mean median
0.005422051 0.005421492

Nearest-Neighbors GAM, Logit
Hyperparameters: hyperparameter_tuning_nn_gam_logit.Rmd
FS 1
caliper = 0.5
calcloset = FALSE
ratio = 3
replace = FALSE
- 4x oversampling
- Interactions across covariates
mean median
0.003007450 0.003011005
Number of Beta samples: 44721

FS 5
caliper = 0
calcloset = FALSE
ratio = 1
replace = FALSE
- 4x oversampling
mean median
0.006251073 0.006245329
Number of Beta samples: 44721

Nearest-Neighbors Probit, Linear
Hyperparameters: hyperparameter_tuning_nn_probit_linear.Rmd
FS 1
caliper = 0.4
calcloset = FALSE
ratio = 3
replace = FALSE
- 4x oversampling
- Interactions across covariates
mean median
0.001773316 0.001777554
Number of Beta samples: 44721

FS 5
caliper = 0
calcloset = FALSE
ratio = 1
replace = FALSE
- 8x oversampling
mean median
0.004524079 0.004523233
Number of Beta samples: 7453

Genetic Matching
Due to the very long training times of genetic matching, feature selection and hyperparameter tuning were not performed using bootstrap sampes. The models were previously trained using the full training sample. The resultant match beta subsets were serialized and uploaded to GCP.
Trained: feature_selection_genmatch_linear.Rmd
Successfully auto-authenticated via moz-fx-dev-cdowhyglund-subBeta-788f8f0d4627.json
Set default bucket name to 'moz-fx-dev-subbeta'
2019-11-21 07:57:28 -- Saved data/milestone2/feature_selection_genmatch_.RData to data/feature_selection_genmatch_.RData (166.4 Mb)
[1] TRUE
FS 1
mean median
0.001500821 0.001506689
Number of Beta samples: 44985

Pop.Size 500
mean median
0.001547905 0.001553225
Number of Beta samples: 44982

FS 3
mean median
0.003216196 0.003224829
Number of Beta samples: 44982

FS 5
mean median
0.003283770 0.003288647
Number of Beta samples: 44982

Results
Genetic matching produces the optimal matching results. It yields the lowest scores, without dropping large sample of the Beta profiles. Unsurprisingly, matching directly, and only, on the performance metrics yields the best results. Increasing the population size hyperparameter slightly decreased model performance, which is surprising. Considering this substantially increases training times, the lower value will be used.
Conclusion: Optimal model framework
- matching algorithm: Genetic Matching
- features: Only performance metrics
- hyperparameters: Default for
Matching library
Push to GCP
The boostrap replicates for validation, oversampled traininig datasets, resultant quantile calculations, and matched datasets are saved to an R image. Then uploaded to the project GCP bucket.
[1] "Previously trained results already exist: data/milestone2/validation_results_20191106.RData"
LS0tCnRpdGxlOiAnTWlsZXN0b25lIDI6IE1vZGVsIFZhbGlkYXRpb24nCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdGhlbWU6IGNvc21vCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwpkYXRlOiAnTGFzdCBVcGRhdGVkOiBgciBmb3JtYXQoU3lzLnRpbWUoKSwgIiVCICVkLCAlWSIpYCcKLS0tCgojIHRsO2RyIApUaGlzIG5vdGVib29rIGRldGVybWluZXMgdGhlIFttb2RlbCBmcmFtZXdvcmtdKGh0dHBzOi8vZG9jcy5nb29nbGUuY29tL2RvY3VtZW50L2QvMVNmdWFudm1ZbXZtRUZBZFE3WjVkakRlTGV6ZE5CMVRFU3FWbWo5M084dG8vZWRpdCNoZWFkaW5nPWguZXgza2s3emQ1YTF5KSB1dGlsaXplZCBmb3IgdGhpcyBbUFJEXShodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9kb2N1bWVudC9kLzFZZ3o2TWt1ZFlIWmpuRG5EOVo5N2tVeUZydlYzS0dXc2pYeVBqZGRoSHEwL2VkaXQjaGVhZGluZz1oLmx2YjlsOGd3Mm5lZSkuIEl0IHV0aWxpemVzIHRoZSByZXN1bHRzIGZyb20gdGhlIGh5cGVycGFyYW1ldGVyIHR1bmluZyBzdGVwLCBhbmQgdHJhaW5zIHRoZSBvcHRpbWFsIG1vZGVsIGZvciBlYWNoIGFsZ29yaXRobSBvbiB0aGUgdjY3IGRhdGFzZXQuIFRoZSByZXN1bHRhbnQgcHJlZGljdGlvbnMgb2YgdGhlc2Ugb3B0aW1hbCBtb2RlbHMgYXJlIGNvbXBhcmVkIHRvIGEgdjY4IHZhbGlkYXRpb24gZGF0YXNldC4gVGhlIGZyYW1ld29yayB1c2VkIHRvIGdlbmVyYXRlIHRoZSBoaWdoZXN0IHBlcmZvcm1hbnQgbW9kZWwgKGUuZy4sIGZlYXR1cmVzLCBoeXBlcnBhcmFtZXRlcnMpIHdpbGwgYmUgdXNlZCBpbiBvbmxpbmUgbW9kZWwgdHJhaW5pbmcgZm9yIHRoZSBzdWJzZXR0aW5nIG9mIEJldGEgaW4gcHJvZHVjdGlvbiB1c2UuCgpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0Kc291cmNlKCcuLi9saWIvc3VwcG9ydGluZ19mdW5jcy5SJykKc291cmNlKCcuLi9saWIvc2NvcmluZy5SJykKbGlicmFyeShNYXRjaEl0KQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkoZ2dyaWRnZXMpCmxpYnJhcnkodmlyaWRpcykKYGBgCgpgYGB7ciBkYXRhX2xvYWQsIGVjaG89RkFMU0V9CmZpbGVfbmFtZSA9ICdkZl90cmFpbl92YWxpZGF0ZV8yMDE5MTAyNS5SRGF0YScKaW1hZ2VfZmlsZV9wYXRoID0gZmlsZS5wYXRoKCdkYXRhJywgZmlsZV9uYW1lKQoKIyBQdWxsIGZyb20gR0NQIGlmIG5lY2Vzc2FyeQppZiAoIWZpbGUuZXhpc3RzKGltYWdlX2ZpbGVfcGF0aCkpewogIFN5cy5zZXRlbnYoIkdDU19ERUZBVUxUX0JVQ0tFVCIgPSAibW96LWZ4LWRldi1zdWJiZXRhIiwKICAgICAgICAgICAiR0NTX0FVVEhfRklMRSIgPSAibW96LWZ4LWRldi1jZG93aHlnbHVuZC1zdWJCZXRhLTc4OGY4ZjBkNDYyNy5qc29uIikKICBsaWJyYXJ5KGdvb2dsZUNsb3VkU3RvcmFnZVIpCiAgZ2NzX2dldF9vYmplY3QoZmlsZS5wYXRoKCdkYXRhJywgJ21pbGVzdG9uZTInLCBmaWxlX25hbWUpLCBzYXZlVG9EaXNrID0gaW1hZ2VfZmlsZV9wYXRoLCBvdmVyd3JpdGUgPSBUUlVFKQp9Cgpsb2FkKGltYWdlX2ZpbGVfcGF0aCkKYGBgCgpgYGB7ciB2YXJfZGVmLCBlY2hvPUZBTFNFfQpkZl9yZWxfdmFsIDwtIGRmX3ZhbGlkYXRlX2YgJT4lCiAgZmlsdGVyKGxhYmVsID09ICdyZWxlYXNlJykKCmRmX2JldGFfdHJhaW4gPC0gZGZfdHJhaW5fZiAlPiUgZmlsdGVyKGlzX3JlbGVhc2UgPT0gRkFMU0UpCmRmX2JldGFfdmFsIDwtIGRmX3ZhbGlkYXRlX2YgJT4lIGZpbHRlcihpc19yZWxlYXNlID09IEZBTFNFKQpkZl9yZWxfdHJhaW4gPC0gZGZfdHJhaW5fZiAlPiUgZmlsdGVyKGlzX3JlbGVhc2UgPT0gVFJVRSkKbl9iZXRhIDwtIG5yb3coZGZfYmV0YV92YWwpCmBgYAoKIyBGZWF0dXJlcwpGZWF0dXJlIHNldHM6IAoKMS4gT25seSBwZXJmb3JtYW5jZSBtZXRyaWNzLgoyLiBQZXJmb3JtYW5jZSBtZXRyaWNzIGFuZCB0aGUgaGlnaGVzdCBmb3VuZCBieSBCb3J1dGEgd2l0aCBvcmlnaW5hbCBkYXRhc2V0LgozLiBQZXJmb3JtYW5jZSBtZXRyaWNzIGFuZCB0aGUgaGlnaGVzdCBmb3VuZCBieSBCb3J1dGEgd2l0aCBlcXVhbCBsYWJlbHMgZGF0YXNldC4KNC4gUGVyZm9ybWFuY2UgbWV0cmljcyBhbmQgYWxsIGNvdmFyaWF0ZXMuCjUuIE9ubHkgdGhlIGhpZ2hlc3QgY292YXJpYXRlcyBmb3VuZCBieSBCb3J1dGEgKGV4Y2x1ZGluZyBwZXJmb3JtYW5jZSkuCjYuIE9ubHkgdGhlIGhpZ2hlc3QgY292YXJpYXRlcyBmb3VuZCBieSBCb3J1dGEgKGVxdWFsIGxhYmVscywgZXhjbHVkaW5nIHBlcmZvcm1hbmNlKS4KNy4gVXRpbGl6ZSBhbGwgY292YXJpYXRlcyAoZXhjbHVkaW5nIHBlcmZvcm1hbmNlKS4gCgoKYGBge3IgYm9ydXRhX2ltcG9ydCwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KZmlsZV9uYW1lID0gJ2ZlYXR1cmVfc2VsZWN0aW9uX2JvcnV0YV9pbml0aWFsXzIwMTkxMDIzLlJEYXRhJwppbWFnZV9maWxlX3BhdGggPSBmaWxlLnBhdGgoJ2RhdGEnLCBmaWxlX25hbWUpCgppZiAoIWZpbGUuZXhpc3RzKGltYWdlX2ZpbGVfcGF0aCkpewogIFN5cy5zZXRlbnYoIkdDU19ERUZBVUxUX0JVQ0tFVCIgPSAibW96LWZ4LWRldi1zdWJiZXRhIiwKICAgICAgICAgICAiR0NTX0FVVEhfRklMRSIgPSAibW96LWZ4LWRldi1jZG93aHlnbHVuZC1zdWJCZXRhLTc4OGY4ZjBkNDYyNy5qc29uIikKICBsaWJyYXJ5KGdvb2dsZUNsb3VkU3RvcmFnZVIpCiAgZ2NzX2dldF9vYmplY3QoZmlsZS5wYXRoKCdkYXRhJywgJ21pbGVzdG9uZTInLCBmaWxlX25hbWUpLCBzYXZlVG9EaXNrID0gaW1hZ2VfZmlsZV9wYXRoLCBvdmVyd3JpdGUgPSBUUlVFKQp9CmxvYWQoaW1hZ2VfZmlsZV9wYXRoKQpgYGAKCmBgYHtyIGJvcnV0YV9mcywgZWNobz1GQUxTRX0KZXh0cmFjdF9ib3J1dGFfZnMgPC0gZnVuY3Rpb24oYm9ydXRhX3JlcywgbnVtPTUpewogIGZlYXR1cmVzIDwtIE5VTEwKICBmb3IobWV0cmljIGluIG5hbWVzKGJvcnV0YV9yZXN1bHRzKSl7CiAgICBmZWF0dXJlcyA8LSBjKG5hbWVzKHNvcnQoYXBwbHkoYm9ydXRhX3Jlc1tbbWV0cmljXV0kSW1wSGlzdG9yeSwgMiwgbWVkaWFuKSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6bnVtXSksIGZlYXR1cmVzKQogIH0KICByZXR1cm4oc29ydCh1bmlxdWUoZmVhdHVyZXMpKSkKfQoKZmVhdHVyZXNfdG9wMTAgPC0gZXh0cmFjdF9ib3J1dGFfZnMoYm9ydXRhX3Jlc3VsdHMsIG51bT0xMCkKZmVhdHVyZXNfdG9wMTBfZXEgPC0gZXh0cmFjdF9ib3J1dGFfZnMoYm9ydXRhX3Jlc3VsdHNfZXEsIG51bT0xMCkKCiMgZmlsdGVyIG91dCBjYXRlZ29yaWNhbApmZWF0dXJlc190b3AxMCA8LSBkZl90cmFpbl9mICU+JSAKICBzZWxlY3QoZmVhdHVyZXNfdG9wMTApICU+JSAKICBzZWxlY3RfaWYoaXMubnVtZXJpYykgJT4lIAogIG5hbWVzKCkKZmVhdHVyZXNfdG9wMTBfZXEgPC0gZGZfdHJhaW5fZiAlPiUgCiAgc2VsZWN0KGZlYXR1cmVzX3RvcDEwX2VxKSAlPiUgCiAgc2VsZWN0X2lmKGlzLm51bWVyaWMpICU+JSAKICBuYW1lcygpCmBgYAoKYGBge3IgZmVhdHVyZV9zZXRzLCBlY2hvPUZBTFNFfQpwZXJmX21ldHJpY3MgPC0gbmFtZXMoZ2V0X20yX21ldHJpY19tYXAoKSkKCmNvdnMgPC0gZGZfdHJhaW5fZiAlPiUKICBzZWxlY3QoLXBlcmZfbWV0cmljcykgJT4lCiAgc2VsZWN0KC1jb250ZW50X2NyYXNoZXMpICU+JQogIHNlbGVjdCgtY2xpZW50X2lkKSAlPiUKICBzZWxlY3QoLWxhYmVsKSAlPiUKICBzZWxlY3QoLWlzX3JlbGVhc2UpICU+JQogIHNlbGVjdCgtYXBwX3ZlcnNpb24pICU+JQogIHNlbGVjdF9pZihpcy5udW1lcmljKSAlPiUgIyBNYWhhbGFub2JpcyBjb25zdHJhaW50CiAgbmFtZXMoKQoKZnMxIDwtIHBlcmZfbWV0cmljcwpmczIgPC0gYyhuYW1lcyhmczEpLCBmZWF0dXJlc190b3AxMCkKZnMzIDwtIGMobmFtZXMoZnMxKSwgZmVhdHVyZXNfdG9wMTBfZXEpCmZzNCA8LSBjKG5hbWVzKGZzMSksIGNvdnMpCmZzNSA8LSBmZWF0dXJlc190b3AxMApmczYgPC0gZmVhdHVyZXNfdG9wMTBfZXEKZnM3IDwtIGNvdnMKYGBgCgojIFZhbGlkYXRpb24gQm9vdHN0cmFwcwoKMzAgYm9vdHN0cmFwIHJlcGxpY2F0ZXMgb2YgdjY4IFJlbGVhc2Ugd2lsbCBiZSB1dGlsaXplZCB0byB2YWxpZGF0ZSB0aGUgbW9kZWxzLiBUaGVyZSBhcmUgYHIgbnJvdyhkZl9yZWxfdmFsKWAgcHJvZmlsZXMgdXRpbGl6ZWQgZm9yIHRoaXMgcHVycG9zZS4gCgpgYGB7ciBidHNfdmFsaWRhdGUsIGVjaG89RkFMU0V9CiMgY3JlYXRlIG9uY2UKZmlsZV9uYW1lID0gJ3ZhbGlkYXRpb25fYnRzXzIwMTkxMTA2LlJEYXRhJwpidHNfZmlsZV9wYXRoID0gZmlsZS5wYXRoKCdkYXRhJywgZmlsZV9uYW1lKQoKaWYgKCFmaWxlLmV4aXN0cyhidHNfZmlsZV9wYXRoKSl7CiAgcHJpbnQoJ0NyZWF0aW5nIHZhbGlkYXRpb24gYm9vdHN0cmFwcyBhbmQgc2F2aW5nJykKICBidHMgPSBsaXN0KCkKICBmb3IoaSBpbiAxOjMwKXsKICAgIGJ0c1tbaV1dIDwtIGRmX3JlbF92YWwgJT4lIAogICAgICBzYW1wbGVfZnJhYyhzaXplID0gMSwgcmVwbGFjZSA9IFRSVUUpICU+JQogICAgICBwdWxsKGNsaWVudF9pZCkgCiAgfQogIHNhdmUoYnRzLCBmaWxlID0gYnRzX2ZpbGVfcGF0aCkKfSBlbHNlIHsKICBwcmludCgnTG9hZGluZyBwcmV2aW91c2x5IGNyZWF0ZWQgYm9vdHN0cmFwcyBmcm9tIGZpbGUnKQogIGxvYWQoYnRzX2ZpbGVfcGF0aCkKfQpgYGAKCmBgYHtyIHNjb3JlciwgZWNobz1GQUxTRX0Kc2NvcmVfbW9kZWwgPC0gZnVuY3Rpb24oYnRzLCBkZl9tYXRjaCwgZGZfdmFsLCB3b3JrZXJzKXsKICBpZiAobWlzc2luZyh3b3JrZXJzKSkgd29ya2VycyA9IGRldGVjdENvcmVzKCkKICBjbCA8LSBtYWtlUFNPQ0tjbHVzdGVyKHdvcmtlcnMpIAogIHJlZ2lzdGVyRG9QYXJhbGxlbChjbCkKICBmaW5hbCA8LSB0cnlDYXRjaCh7CiAgICBzY29yZXMgPC0gZm9yZWFjaChpPTE6bGVuZ3RoKGJ0cyksIAogICAgICAgICAgICAgICAgICAgICAgLnBhY2thZ2VzID0gYygnZHBseXInLCAndHJhbnNwb3J0JyksIAogICAgICAgICAgICAgICAgICAgICAgLmV4cG9ydD1jKCdjYWxjX3Njb3JlJywgJ2NhbGNfY21zJywgJ2dldF9tMl9tZXRyaWNfbWFwJykpICVkb3BhciUgewogICAgICAgICAgICAgICAgICAgICAgICBidCA8LSBidHNbW2ldXQogICAgICAgICAgICAgICAgICAgICAgICB0ZXN0IDwtIGRmX3ZhbCAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgcmlnaHRfam9pbihkYXRhLmZyYW1lKGNsaWVudF9pZCA9IGJ0LCBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKSwgYnk9J2NsaWVudF9pZCcsICdyaWdodCcpCiAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICBkZl9zY29yZXMgPC0gdGVzdCAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICBiaW5kX3Jvd3MoZGZfbWF0Y2gpCiAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICBzY29yZSA8LSBjYWxjX3Njb3JlKGRmX3Njb3JlcywgZ2V0X20yX21ldHJpY19tYXAoKSkKICAgICAgICAgICAgICAgICAgICAgICAgc2NvcmUKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgc2NvcmVzIDwtIHVubGlzdChzY29yZXMpCiAgICBjKG1lYW4gPSBtZWFuKHNjb3JlcyksIG1lZGlhbiA9IG1lZGlhbihzY29yZXMpKQogIH0sIAogIGVycm9yID0gZnVuY3Rpb24oY29uZCl7CiAgICBtZXNzYWdlKHBhc3RlKCJCb290c3RyYXAgdmFsaWRhdGlvbiBmYWlsZWQ6ICIsIGNvbmQpKQogICAgcmV0dXJuKE5BKQogIH0sCiAgZmluYWxseSA9IHsKICAgIHN0b3BDbHVzdGVyKGNsKQogIH0KICApCiAgcmV0dXJuKGZpbmFsKQp9CgpidWlsZF9xdWFudGlsZV9kZiA8LSBmdW5jdGlvbih2YWxpZGF0aW9uLCBtYXRjaGVkLCBvcmlnaW5hbCl7CiAgcXFzIDwtIGxpc3QoKQogIGZvciAocGVyZl9tZXRyaWMgaW4gcGVyZl9tZXRyaWNzKXsKICAgIHFxIDwtIHFxcGxvdCh2YWxpZGF0aW9uW1twZXJmX21ldHJpY11dLCBtYXRjaGVkW1twZXJmX21ldHJpY11dLCBwbG90Lml0ID0gRkFMU0UpICU+JSAKICAgICAgYmluZF9yb3dzKCkgJT4lCiAgICAgIG11dGF0ZSh0eXBlID0gJ21hdGNoZWQnKQogICAgcXFfZnVsbCA8LSBxcXBsb3QodmFsaWRhdGlvbltbcGVyZl9tZXRyaWNdXSwgb3JpZ2luYWxbW3BlcmZfbWV0cmljXV0sIHBsb3QuaXQgPSBGQUxTRSkgJT4lIAogICAgICBiaW5kX3Jvd3MoKSAlPiUKICAgICAgbXV0YXRlKHR5cGUgPSAnb3JpZ2luYWwnKSAlPiUKICAgICAgYmluZF9yb3dzKHFxKSAlPiUKICAgICAgbXV0YXRlKG1ldHJpYyA9IHBlcmZfbWV0cmljKQogICAgIyBxcV9mdWxsJG1ldHJpYyA8LSBwZXJmX21ldHJpYwogICAgcXFzW1twZXJmX21ldHJpY11dIDwtIHFxX2Z1bGwKICB9CiAgcXFfZGYgPC0gcXFzICU+JSBiaW5kX3Jvd3MoKSAlPiUgcmVuYW1lKHJlbGVhc2UgPSB4LCBiZXRhID0geSkKICByZXR1cm4ocXFfZGYpCn0KCnBsb3RfdmFsaWRhdGlvbl9yZXN1bHRzIDwtIGZ1bmN0aW9uKHZhbGlkYXRpb24sIG1hdGNoZWQsIG9yaWdpbmFsLCBxcV9kZil7CiAgaWYgKG1pc3NpbmcocXFfZGYpKSBxcV9kZiA8LSBidWlsZF9xdWFudGlsZV9kZih2YWxpZGF0aW9uLCBtYXRjaGVkLCBvcmlnaW5hbCkKICAKICBkZiA8LSBtYXRjaGVkICU+JQogICAgICBtdXRhdGUobGFiZWwgPSAnYmV0YSAtIG1hdGNoZWQnKSAlPiUKICAgICAgYmluZF9yb3dzKHZhbGlkYXRpb24pICU+JQogICAgICBiaW5kX3Jvd3Mob3JpZ2luYWwpICU+JQogICAgICBzZWxlY3QocGVyZl9tZXRyaWNzLCBsYWJlbCkgIyAlPiUKICAgICAgIyBnYXRoZXIoa2V5ID0gJ21ldHJpYycsIHZhbHVlID0gJ21lYXN1cmVtZW50JywgLWxhYmVsKQogIAogIHBsb3RzIDwtIGxpc3QoKQogIGZvciAocG1ldCBpbiBwZXJmX21ldHJpY3MpewogICAgcF9xcSA8LSBnZ3Bsb3QocXFfZGYgJT4lIGZpbHRlcihtZXRyaWMgPT0gcG1ldCksIGFlcyh4ID0gcmVsZWFzZSwgeSA9IGJldGEpKSArCiAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gdHlwZSwgc2hhcGUgPSB0eXBlKSkgKyAKICAgICAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxLCBpbnRlcmNlcHQgPSAwKSArIAogICAgICB0aGVtZV9idygpICsgCiAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCksCiAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44LCAwLjIpKSArIAogICAgICBnZ3RpdGxlKHBtZXQpIAogICAgCiAgICBwX3JpZGdlIDwtIGdncGxvdChkZiwgYWVzKHg9ISFzeW0ocG1ldCksIHk9bGFiZWwsIGZpbGw9ZmFjdG9yKC4ucXVhbnRpbGUuLikpKSArCiAgICBzdGF0X2RlbnNpdHlfcmlkZ2VzKAogICAgICBnZW9tID0gImRlbnNpdHlfcmlkZ2VzX2dyYWRpZW50IiwgY2FsY19lY2RmID0gVFJVRSwKICAgICAgcXVhbnRpbGVzID0gMTAsIHF1YW50aWxlX2xpbmVzID0gVFJVRQogICAgKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGUgPSBUUlVFLCBuYW1lID0gIlF1YXJ0aWxlcyIpICsgCiAgICAgIHRoZW1lX2J3KCkgKwogICAgICB4bGFiKHBtZXQpICsgCiAgICAgIHhsaW0oYygwLCAxMDAwMCkpICsgCiAgICAgIGd1aWRlcyhmaWxsID0gRkFMU0UpCiAgICAKICAgIHBsb3RzW1twYXN0ZShwbWV0LCAncXEnLCBzZXA9Il8iKV1dIDwtIHBfcXEKICAgIHBsb3RzW1twYXN0ZShwbWV0LCAncmlkZ2UnLCBzZXA9Il8iKV1dIDwtIHBfcmlkZ2UKICB9CiAgCiAgcHJpbnQocGxvdF9ncmlkKHBsb3RsaXN0ID0gcGxvdHMsIG5jb2wgPSAyKSkKfQpgYGAKCiMgT3ZlcnNhbXBsaW5nCgpUaGUgZW50aXJlIHY2NyBkYXRhc2V0IHdpbGwgYmUgdXRpbGl6ZWQgZm9yIHRyYWluaW5nLiBIb3dldmVyLCBtb3N0IG9mIHRoZSBtYXRjaGluZyBhbGdvcml0aG1zIGFyZSBtb3JlIHBlcmZvcm1hbnQgd2l0aCBoaWdoZXIgcmF0aW9zIG9mIEJldGEgdG8gUmVsZWFzZS4gVGhlcmVmb3JlLCB2YXJpb3VzIGxldmVscyBvZiBvdmVyc2FtcGxlZCBkYXRhc2V0cyBhcmUgY3JlYXRlZCBmb3IgdHJhaW5pbmcuIFRoZXJlIGFyZSBgciBuX2JldGFgIHByb2ZpbGVzIHV0aWxpemVkIGZvciB0aGlzIHB1cnBvc2UuCgpgYGB7ciBvdmVyc2FtcGxpbmcsIGVjaG89RkFMU0V9CiMgY3JlYXRlIG9uY2UKCmZpbGVfbmFtZSA9ICd0cmFpbmluZ19maW5hbF9vdmVyc2FtcGxlc18yMDE5MTEwNi5SRGF0YScKb3ZlcnNhbXBsZXNfZmlsZV9wYXRoID0gZmlsZS5wYXRoKCdkYXRhJywgZmlsZV9uYW1lKQoKaWYgKCFmaWxlLmV4aXN0cyhvdmVyc2FtcGxlc19maWxlX3BhdGgpKXsKICBwcmludCgnQ3JlYXRpbmcgb3ZlcnNhbXBsaW5nIHRyYWluaW5nIHNldHMgYW5kIHNhdmluZycpCiAgb3ZlcnNhbXBsZSA8LSBmdW5jdGlvbihvdmVyc2FtcGxpbmcsIGRmX2JldGEsIGRmX3JlbCkgewogICAgZGZfeCA8LSBkZl9yZWwgJT4lCiAgICAgIHNhbXBsZV9uKHNpemUgPSByb3VuZChuX2JldGEgLyBvdmVyc2FtcGxpbmcpKSAlPiUKICAgICAgcmJpbmQoZGZfYmV0YSkKICAgIHJldHVybihkZl94KQogICAgfQogIAogIG92ZXJzYW1wbGVzIDwtIGMoMSwgMiwgNCwgOCwgMTYpCiAgZGZzIDwtIGxhcHBseShvdmVyc2FtcGxlcywgb3ZlcnNhbXBsZSwgZGZfYmV0YSA9IGRmX2JldGFfdHJhaW4sIGRmX3JlbCA9IGRmX3JlbF90cmFpbikKICBuYW1lcyhkZnMpIDwtIGFzLmNoYXJhY3RlcihvdmVyc2FtcGxlcykKICBzYXZlKGRmcywgZmlsZSA9IG92ZXJzYW1wbGVzX2ZpbGVfcGF0aCkKfSBlbHNlIHsKICBwcmludCgnTG9hZGluZyBwcmV2aW91c2x5IGNyZWF0ZWQgb3ZlcnNhbXBsaW5nIHRyYWluaW5nIHNldHMgZnJvbSBmaWxlJykKICBsb2FkKG92ZXJzYW1wbGVzX2ZpbGVfcGF0aCkKfQpgYGAKCiMgTW9kZWwgVHJhaW5pbmcKClR3byBtYXRjaGluZyBtZXRob2RzIHdlcmUgdWx0aW1hdGVseSB0ZXN0ZWQsIGFzIENFTSBhbmQgc3ViY2xhc3Npbmcgd2VyZSBwcm9kdWNpbmcgZXh0cmVtZWx5IHBvb3IgcmVzdWx0cy4gCgoqIG5lYXJlc3QtbmVpZ2hib3JzCiogZ2VuZXRpYyBtYXRjaGluZwoKRm9yIGFsbCBtb2RlbHMgdGhlIGZvbGxvd2luZyBkaWFnbm9zdGljcyBhcmUgcmVwb3J0ZWQ6CgoqIG1lYW4sIG1lZGlhbiBzY29yZSBhZ2FpbnN0IHRoZSAzMCB2YWxpZGF0aW9uIHJlcGxpY2F0ZXMKKiBOdW1iZXIgb2YgbWF0Y2hlZCBCZXRhIHNhbXBsZXMKKiBRUSBhbmQgcmlkZ2UgcGxvdHMgb2Ygb3JpZ2luYWwgYW5kIG1hdGNoZWQgQmV0YSBzYW1wbGVzCgojIyBOZWFyZXN0LU5laWdoYm9ycywgTWFsYWhhbm9iaXMgCgpIeXBlcnBhcmFtZXRlcnM6IGBoeXBlcnBhcmFtZXRlcl90dW5pbmdfbm5fbWFsYWhhbm9iaXMuUm1kYAoKRmVhdHVyZSBzZWxlY3Rpb24gYW5kIGh5cGVycGFyYW1ldGVyIHR1bmluZyBmb3IgdGhlIG5lYXJlc3QtbmVpZ2hib3JzIG1vZGVscyB3ZXJlIGFsbCB0cmFpbmVkIHVzaW5nIHRoZSBgTWF0Y2hJdGAgbGlicmFyeS4gIFRoZXNlIG1vZGVscyBhcmUgdHJhaW5lZCB1c2luZyB0aGUgZnVsbCwgcmVsZXZhbnRseSBvdmVyc2FtcGxlZCwgdHJhaW5pbmcgZGF0YXNldC4gIAoKYGBge3IgdHJhaW5lciwgZWNobz1GQUxTRX0KdHJhaW5fbWF0Y2hpdCA8LSBmdW5jdGlvbih0cmFpbiwgbW9kZWxfY292cywgYWRkX2ludGVyYWN0aW9ucywgLi4uKXsKICAjIHRyYWluIG1vZGVsCiAgZm9ybXVsYSA8LSBnZW5lcmF0ZV9mb3JtdWxhKG1vZGVsX2NvdnMsIGxhYmVsID0gJ2lzX3JlbGVhc2UnLCBhZGRfaW50ZXJhY3Rpb25zKQogIG1vZGVsIDwtIG1hdGNoaXQoZm9ybXVsYSwgdHJhaW4sIC4uLikKICAKICAjIGV4dHJhY3QgYmV0YSBzdWJzZXQKICBkZl9tYXRjaGVkIDwtIGdldF9tYXRjaGVzKG1vZGVsLCB0cmFpbikgJT4lCiAgICBzZWxlY3QoLXdlaWdodHMsIC1kaXN0YW5jZSkgJT4lCiAgICBmaWx0ZXIobGFiZWwgPT0gJ2JldGEnKQogIAogIHJldHVybihsaXN0KG1vZGVsID0gbW9kZWwsIG1hdGNoZWQgPSBkZl9tYXRjaGVkKSkKfQoKYGBgCgojIyMgRlMgMQoKKiBgY2FsaXBlcmAgPSAwLjIwCiogYGNhbGNsb3NldGAgPSBUUlVFCiogYHJhdGlvYCA9IDIKKiBgcmVwbGFjZWAgPSBGQUxTRQoqIDR4IG92ZXJzYW1wbGluZwoKYGBge3Igbm5fbWFsX2ZzMSwgZWNobz1GQUxTRX0Kbm4ubWFsLmZzMSA8LSB0cmFpbl9tYXRjaGl0KGRmc1tbJzQnXV0sIGZzMSwgYWRkX2ludGVyYWN0aW9ucyA9IEZBTFNFLCByZXBsYWNlID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgICBjYWxpcGVyID0gMC4yNSwgY2FsY2xvc2VzdCA9IFRSVUUsIHJhdGlvID0gMiwgZGlzdGFuY2UgPSAibWFoYWxhbm9iaXMiKQpubi5tYWwuZnMxLnFxIDwtIGJ1aWxkX3F1YW50aWxlX2RmKGRmX3JlbF92YWwsIG5uLm1hbC5mczEkbWF0Y2hlZCwgZGZfYmV0YV92YWwpCm5uLm1hbC5mczEuc2NvcmUgPC0gc2NvcmVfbW9kZWwoYnRzLCBubi5tYWwuZnMxJG1hdGNoZWQsIGRmX3JlbF92YWwpCm5uLm1hbC5mczEuc2NvcmUKYGBgCgpOdW1iZXIgb2YgQmV0YSBzYW1wbGVzOiBgciBucm93KG5uLm1hbC5mczEkbWF0Y2hlZClgCgpgYGB7ciBubl9tYWxfZnMxX3ZhbF9wbHQsIGZpZy53aWR0aD0xNSxmaWcuaGVpZ2h0PTI1LCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlY2hvPUZBTFNFfQpwbG90X3ZhbGlkYXRpb25fcmVzdWx0cyhkZl9yZWxfdmFsLCBubi5tYWwuZnMxJG1hdGNoZWQsIGRmX2JldGFfdmFsLCBubi5tYWwuZnMxLnFxKQpgYGAKCiMjIyBGUyAzIAoKKiBgY2FsaXBlcmAgPSAzLjAwCiogYGNhbGNsb3NldGAgPSBUUlVFCiogYHJhdGlvYCA9IDEKKiBgcmVwbGFjZWAgPSBUUlVFCiogMTZ4IG92ZXJzYW1wbGluZwoKYGBge3Igbm5fbWFsX2ZzMywgZWNobz1GQUxTRX0Kbm4ubWFsLmZzMyA8LSB0cmFpbl9tYXRjaGl0KGRmc1tbJzE2J11dLCBmczMsIGFkZF9pbnRlcmFjdGlvbnMgPSBGQUxTRSwgcmVwbGFjZSA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICBjYWxpcGVyID0gMy4wMCwgY2FsY2xvc2VzdCA9IFRSVUUsIHJhdGlvID0gMSwgZGlzdGFuY2UgPSAibWFoYWxhbm9iaXMiKQpubi5tYWwuZnMzLnFxIDwtIGJ1aWxkX3F1YW50aWxlX2RmKGRmX3JlbF92YWwsIG5uLm1hbC5mczMkbWF0Y2hlZCwgZGZfYmV0YV92YWwpCm5uLm1hbC5mczMuc2NvcmUgPC0gc2NvcmVfbW9kZWwoYnRzLCBubi5tYWwuZnMzJG1hdGNoZWQsIGRmX3JlbF92YWwpCm5uLm1hbC5mczMuc2NvcmUKYGBgCk51bWJlciBvZiBCZXRhIHNhbXBsZXM6IGByIG5yb3cobm4ubWFsLmZzMyRtYXRjaGVkKWAKCmBgYHtyIG5uX21hbF9mczNfdmFsX3BsdCwgZmlnLndpZHRoPTE1LGZpZy5oZWlnaHQ9MjUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVjaG89RkFMU0V9CnBsb3RfdmFsaWRhdGlvbl9yZXN1bHRzKGRmX3JlbF92YWwsIG5uLm1hbC5mczMkbWF0Y2hlZCwgZGZfYmV0YV92YWwpCmBgYAoKCgojIyMgRlMgNQoKKiBgY2FsaXBlcmAgPSAwCiogYGNhbGNsb3NldGAgPSBUUlVFCiogYHJhdGlvYCA9IDEKKiBgcmVwbGFjZWAgPSBGQUxTRQoqIDh4IG92ZXJzYW1wbGluZwoKYGBge3Igbm5fbWFsX2ZzNSwgZWNobz1GQUxTRX0Kbm4ubWFsLmZzNSA8LSB0cmFpbl9tYXRjaGl0KGRmc1tbJzgnXV0sIGZzNSwgYWRkX2ludGVyYWN0aW9ucyA9IEZBTFNFLCByZXBsYWNlID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGNhbGlwZXIgPSAwLCBjYWxjbG9zZXN0ID0gVFJVRSwgcmF0aW8gPSAxLCAKICAgICAgICAgICAgICAgICAgICAgICAgZGlzdGFuY2UgPSAibWFoYWxhbm9iaXMiKQpubi5tYWwuZnM1LnFxIDwtIGJ1aWxkX3F1YW50aWxlX2RmKGRmX3JlbF92YWwsIG5uLm1hbC5mczUkbWF0Y2hlZCwgZGZfYmV0YV92YWwpCm5uLm1hbC5mczUuc2NvcmUgPC0gc2NvcmVfbW9kZWwoYnRzLCBubi5tYWwuZnM1JG1hdGNoZWQsIGRmX3JlbF92YWwpCm5uLm1hbC5mczUuc2NvcmUKYGBgCgpOdW1iZXIgb2YgQmV0YSBzYW1wbGVzOiBgciBucm93KG5uLm1hbC5mczUkbWF0Y2hlZClgCgpgYGB7ciBubl9tYWxfZnM1X3ZhbF9wbHQsIGZpZy53aWR0aD0xNSxmaWcuaGVpZ2h0PTI1LCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlY2hvPUZBTFNFfQpwbG90X3ZhbGlkYXRpb25fcmVzdWx0cyhkZl9yZWxfdmFsLCBubi5tYWwuZnM1JG1hdGNoZWQsIGRmX2JldGFfdmFsKQpgYGAKCiMjIE5lYXJlc3QtTmVpZ2hib3JzIExvZ2l0LCBMaW5lYXIKCkh5cGVycGFyYW1ldGVyczogYGh5cGVycGFyYW1ldGVyX3R1bmluZ19ubl9sb2dpdF9saW5lYXIuUm1kYAoKYGBge3IgZmVhdHVyZV9zZXRzX25uLCBlY2hvPUZBTFNFfQojIFJlbG9hZCB0byBhZGQgYWRkaXRpb25hbCBjYXRlZ29yaWNhbCBjb3ZhcmlhdGVzCgpwZXJmX21ldHJpY3MgPC0gbmFtZXMoZ2V0X20yX21ldHJpY19tYXAoKSkKCmNvdnMgPC0gZGZfdHJhaW5fZiAlPiUKICBzZWxlY3QoLXBlcmZfbWV0cmljcykgJT4lCiAgc2VsZWN0KC1jb250ZW50X2NyYXNoZXMpICU+JQogIHNlbGVjdCgtY2xpZW50X2lkKSAlPiUKICBzZWxlY3QoLWxhYmVsKSAlPiUKICBzZWxlY3QoLWlzX3JlbGVhc2UpICU+JQogIHNlbGVjdCgtYXBwX3ZlcnNpb24pICU+JQogIG5hbWVzKCkKCmZzMSA8LSBwZXJmX21ldHJpY3MKZnMyIDwtIGMobmFtZXMoZnMxKSwgZmVhdHVyZXNfdG9wMTApCmZzMyA8LSBjKG5hbWVzKGZzMSksIGZlYXR1cmVzX3RvcDEwX2VxKQpmczQgPC0gYyhuYW1lcyhmczEpLCBjb3ZzKQpmczUgPC0gZmVhdHVyZXNfdG9wMTAKZnM2IDwtIGZlYXR1cmVzX3RvcDEwX2VxCmZzNyA8LSBjb3ZzCmBgYAoKIyMjIEZTIDEKCiogYGNhbGlwZXJgID0gMC40CiogYGNhbGNsb3NldGAgPSBUUlVFCiogYHJhdGlvYCA9IDMKKiBgcmVwbGFjZWAgPSBGQUxTRQoqIDR4IG92ZXJzYW1wbGluZwoqIEludGVyYWN0aW9ucyBhY3Jvc3MgY292YXJpYXRlcwoKYGBge3Igbm5fbG9naXRfZnMxLCB3YXJuaW5nPUZBTFNFLCBlcnJvcj1GQUxTRSwgZWNobz1GQUxTRX0Kbm4ubGluZWFyLmxvZ2l0LmZzMSA8LSB0cmFpbl9tYXRjaGl0KGRmc1tbJzQnXV0sIGZzMSwgYWRkX2ludGVyYWN0aW9ucyA9IFRSVUUsIHJlcGxhY2UgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGNhbGlwZXIgPSAwLjQsIGNhbGNsb3Nlc3QgPSBUUlVFLCByYXRpbyA9IDMsIGRpc3RhbmNlID0gImxpbmVhci5sb2dpdCIpCm5uLmxpbmVhci5sb2dpdC5mczEucXEgPC0gYnVpbGRfcXVhbnRpbGVfZGYoZGZfcmVsX3ZhbCwgbm4ubGluZWFyLmxvZ2l0LmZzMSRtYXRjaGVkLCBkZl9iZXRhX3ZhbCkKbm4ubGluZWFyLmxvZ2l0LmZzMS5zY29yZSA8LSBzY29yZV9tb2RlbChidHMsIG5uLmxpbmVhci5sb2dpdC5mczEkbWF0Y2hlZCwgZGZfcmVsX3ZhbCkKbm4ubGluZWFyLmxvZ2l0LmZzMS5zY29yZQpgYGAKCk51bWJlciBvZiBCZXRhIHNhbXBsZXM6IGByIG5yb3cobm4ubGluZWFyLmxvZ2l0LmZzMSRtYXRjaGVkKWAKCmBgYHtyIG5uX2xvZ2l0X2ZzMV92YWxfcGx0LCBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD0yNSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0KcGxvdF92YWxpZGF0aW9uX3Jlc3VsdHMoZGZfcmVsX3ZhbCwgbm4ubGluZWFyLmxvZ2l0LmZzMSRtYXRjaGVkLCBkZl9iZXRhX3ZhbCkKYGBgCgojIyMgRlMgNQoKKiBgY2FsaXBlcmAgPSAwCiogYGNhbGNsb3NldGAgPSBGQUxTRQoqIGByYXRpb2AgPSAxCiogYHJlcGxhY2VgID0gRkFMU0UKKiA4eCBvdmVyc2FtcGxpbmcKCmBgYHtyIG5uX2xvZ2l0X2ZzMywgd2FybmluZz1GQUxTRSwgZXJyb3I9RkFMU0UsIGVjaG89RkFMU0V9Cm5uLmxpbmVhci5sb2dpdC5mczUgPC0gdHJhaW5fbWF0Y2hpdChkZnNbWyc4J11dLCBmczUsIGFkZF9pbnRlcmFjdGlvbnMgPSBGQUxTRSwgcmVwbGFjZSA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgICAgY2FsaXBlciA9IDAsIGNhbGNsb3Nlc3QgPSBGQUxTRSwgcmF0aW8gPSAxLCBkaXN0YW5jZSA9ICJsaW5lYXIubG9naXQiKQpubi5saW5lYXIubG9naXQuZnM1LnFxIDwtIGJ1aWxkX3F1YW50aWxlX2RmKGRmX3JlbF92YWwsIG5uLmxpbmVhci5sb2dpdC5mczUkbWF0Y2hlZCwgZGZfYmV0YV92YWwpCm5uLmxpbmVhci5sb2dpdC5mczUuc2NvcmUgPC0gc2NvcmVfbW9kZWwoYnRzLCBubi5saW5lYXIubG9naXQuZnM1JG1hdGNoZWQsIGRmX3JlbF92YWwpCm5uLmxpbmVhci5sb2dpdC5mczUuc2NvcmUKYGBgCgpgYGB7ciBubl9sb2dpdF9mczVfdmFsX3BsdCwgZmlnLndpZHRoPTE1LGZpZy5oZWlnaHQ9MjUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVjaG89RkFMU0V9CnBsb3RfdmFsaWRhdGlvbl9yZXN1bHRzKGRmX3JlbF92YWwsIG5uLmxpbmVhci5sb2dpdC5mczUkbWF0Y2hlZCwgZGZfYmV0YV92YWwpCmBgYAoKIyMgTmVhcmVzdC1OZWlnaGJvcnMgR0FNLCBMb2dpdAoKSHlwZXJwYXJhbWV0ZXJzOiBgaHlwZXJwYXJhbWV0ZXJfdHVuaW5nX25uX2dhbV9sb2dpdC5SbWRgCgojIyMgRlMgMQoKKiBgY2FsaXBlcmAgPSAwLjUKKiBgY2FsY2xvc2V0YCA9IEZBTFNFCiogYHJhdGlvYCA9IDMKKiBgcmVwbGFjZWAgPSBGQUxTRQoqIDR4IG92ZXJzYW1wbGluZwoqIEludGVyYWN0aW9ucyBhY3Jvc3MgY292YXJpYXRlcwoKYGBge3Igbm5fZ2FtX2ZzMSwgd2FybmluZz1GQUxTRSwgZXJyb3I9RkFMU0UsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0V9Cm5uLmdhbS5mczEgPC0gdHJhaW5fbWF0Y2hpdChkZnNbWyc0J11dLCBmczEsIGFkZF9pbnRlcmFjdGlvbnMgPSBUUlVFLCByZXBsYWNlID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgICBjYWxpcGVyID0gMC41LCBjYWxjbG9zZXN0ID0gVFJVRSwgcmF0aW8gPSAzLCBkaXN0YW5jZSA9ICJHQU1sb2dpdCIpCm5uLmdhbS5mczEucXEgPC0gYnVpbGRfcXVhbnRpbGVfZGYoZGZfcmVsX3ZhbCwgbm4uZ2FtLmZzMSRtYXRjaGVkLCBkZl9iZXRhX3ZhbCkKbm4uZ2FtLmZzMS5zY29yZSA8LSBzY29yZV9tb2RlbChidHMsIG5uLmdhbS5mczEkbWF0Y2hlZCwgZGZfcmVsX3ZhbCkKbm4uZ2FtLmZzMS5zY29yZQpgYGAKCk51bWJlciBvZiBCZXRhIHNhbXBsZXM6IGByIG5yb3cobm4uZ2FtLmZzMSRtYXRjaGVkKWAKCmBgYHtyIG5uX2dhbV9mczFfdmFsX3BsdCwgZmlnLndpZHRoPTE1LGZpZy5oZWlnaHQ9MjUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVjaG89RkFMU0V9CnBsb3RfdmFsaWRhdGlvbl9yZXN1bHRzKGRmX3JlbF92YWwsIG5uLmdhbS5mczEkbWF0Y2hlZCwgZGZfYmV0YV92YWwpCmBgYAoKCiMjIyBGUyA1CgoqIGBjYWxpcGVyYCA9IDAKKiBgY2FsY2xvc2V0YCA9IEZBTFNFCiogYHJhdGlvYCA9IDEKKiBgcmVwbGFjZWAgPSBGQUxTRQoqIDR4IG92ZXJzYW1wbGluZwoKYGBge3Igbm5fZ2FtX2ZzNSwgd2FybmluZz1GQUxTRSwgZXJyb3I9RkFMU0UsIGVjaG89RkFMU0V9Cm5uLmdhbS5mczUgPC0gdHJhaW5fbWF0Y2hpdChkZnNbWyc0J11dLCBmczUsIGFkZF9pbnRlcmFjdGlvbnMgPSBGQUxTRSwgcmVwbGFjZSA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgICAgY2FsaXBlciA9IDAsIGNhbGNsb3Nlc3QgPSBGQUxTRSwgcmF0aW8gPSAxLCBkaXN0YW5jZSA9ICJHQU1sb2dpdCIpCm5uLmdhbS5mczUucXEgPC0gYnVpbGRfcXVhbnRpbGVfZGYoZGZfcmVsX3ZhbCwgbm4uZ2FtLmZzNSRtYXRjaGVkLCBkZl9iZXRhX3ZhbCkKbm4uZ2FtLmZzNS5zY29yZSA8LSBzY29yZV9tb2RlbChidHMsIG5uLmdhbS5mczUkbWF0Y2hlZCwgZGZfcmVsX3ZhbCkKbm4uZ2FtLmZzNS5zY29yZQpgYGAKCk51bWJlciBvZiBCZXRhIHNhbXBsZXM6IGByIG5yb3cobm4uZ2FtLmZzMSRtYXRjaGVkKWAKCmBgYHtyIG5uX2dhbV9mczVfdmFsX3BsdCwgZmlnLndpZHRoPTE1LGZpZy5oZWlnaHQ9MjUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVjaG89RkFMU0V9CnBsb3RfdmFsaWRhdGlvbl9yZXN1bHRzKGRmX3JlbF92YWwsIG5uLmdhbS5mczUkbWF0Y2hlZCwgZGZfYmV0YV92YWwpCmBgYAoKIyMgTmVhcmVzdC1OZWlnaGJvcnMgUHJvYml0LCBMaW5lYXIKCkh5cGVycGFyYW1ldGVyczogYGh5cGVycGFyYW1ldGVyX3R1bmluZ19ubl9wcm9iaXRfbGluZWFyLlJtZGAKCiMjIyBGUyAxCgoqIGBjYWxpcGVyYCA9IDAuNAoqIGBjYWxjbG9zZXRgID0gRkFMU0UKKiBgcmF0aW9gID0gMwoqIGByZXBsYWNlYCA9IEZBTFNFCiogNHggb3ZlcnNhbXBsaW5nCiogSW50ZXJhY3Rpb25zIGFjcm9zcyBjb3ZhcmlhdGVzCgpgYGB7ciBubl9wcm9iaXRfZnMxLCB3YXJuaW5nPUZBTFNFLCBlcnJvcj1GQUxTRSwgZWNobz1GQUxTRX0Kbm4ubGluZWFyLnByb2JpdC5mczEgPC0gdHJhaW5fbWF0Y2hpdChkZnNbWyc0J11dLCBmczEsIGFkZF9pbnRlcmFjdGlvbnMgPSBUUlVFLCByZXBsYWNlID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgICBjYWxpcGVyID0gMC40LCBjYWxjbG9zZXN0ID0gVFJVRSwgcmF0aW8gPSAzLCBkaXN0YW5jZSA9ICJsaW5lYXIucHJvYml0IikKbm4ubGluZWFyLnByb2JpdC5mczEucXEgPC0gYnVpbGRfcXVhbnRpbGVfZGYoZGZfcmVsX3ZhbCwgbm4ubGluZWFyLnByb2JpdC5mczEkbWF0Y2hlZCwgZGZfYmV0YV92YWwpCm5uLmxpbmVhci5wcm9iaXQuZnMxLnNjb3JlIDwtIHNjb3JlX21vZGVsKGJ0cywgbm4ubGluZWFyLnByb2JpdC5mczEkbWF0Y2hlZCwgZGZfcmVsX3ZhbCkKbm4ubGluZWFyLnByb2JpdC5mczEuc2NvcmUKYGBgCgpOdW1iZXIgb2YgQmV0YSBzYW1wbGVzOiBgciBucm93KG5uLmxpbmVhci5wcm9iaXQuZnMxJG1hdGNoZWQpYAoKYGBge3Igbm5fcHJvYml0X2ZzMV92YWxfcGx0LCBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD0yNSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0KcGxvdF92YWxpZGF0aW9uX3Jlc3VsdHMoZGZfcmVsX3ZhbCwgbm4ubGluZWFyLnByb2JpdC5mczEkbWF0Y2hlZCwgZGZfYmV0YV92YWwpCmBgYAoKIyMjIEZTIDUKCiogYGNhbGlwZXJgID0gMAoqIGBjYWxjbG9zZXRgID0gRkFMU0UKKiBgcmF0aW9gID0gMQoqIGByZXBsYWNlYCA9IEZBTFNFCiogOHggb3ZlcnNhbXBsaW5nCgpgYGB7ciBubl9wcm9iaXRfZnM1LCB3YXJuaW5nPUZBTFNFLCBlcnJvcj1GQUxTRSwgZWNobz1GQUxTRX0Kbm4ubGluZWFyLnByb2JpdC5mczUgPC0gdHJhaW5fbWF0Y2hpdChkZnNbWyc4J11dLCBmczUsIGFkZF9pbnRlcmFjdGlvbnMgPSBGQUxTRSwgcmVwbGFjZSA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgICAgY2FsaXBlciA9IDAsIGNhbGNsb3Nlc3QgPSBGQUxTRSwgcmF0aW8gPSAxLCBkaXN0YW5jZSA9ICJsaW5lYXIucHJvYml0IikKbm4ubGluZWFyLnByb2JpdC5mczUucXEgPC0gYnVpbGRfcXVhbnRpbGVfZGYoZGZfcmVsX3ZhbCwgbm4ubGluZWFyLnByb2JpdC5mczUkbWF0Y2hlZCwgZGZfYmV0YV92YWwpCm5uLmxpbmVhci5wcm9iaXQuZnM1LnNjb3JlIDwtIHNjb3JlX21vZGVsKGJ0cywgbm4ubGluZWFyLnByb2JpdC5mczUkbWF0Y2hlZCwgZGZfcmVsX3ZhbCkKbm4ubGluZWFyLnByb2JpdC5mczUuc2NvcmUKYGBgCgpOdW1iZXIgb2YgQmV0YSBzYW1wbGVzOiBgciBucm93KG5uLmxpbmVhci5wcm9iaXQuZnM1JG1hdGNoZWQpYAoKYGBge3Igbm5fcHJvYml0X2ZzNV92YWxfcGx0LCBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD0yNSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0KcGxvdF92YWxpZGF0aW9uX3Jlc3VsdHMoZGZfcmVsX3ZhbCwgbm4ubGluZWFyLnByb2JpdC5mczUkbWF0Y2hlZCwgZGZfYmV0YV92YWwpCmBgYAoKIyMgR2VuZXRpYyBNYXRjaGluZwoKRHVlIHRvIHRoZSB2ZXJ5IGxvbmcgdHJhaW5pbmcgdGltZXMgb2YgZ2VuZXRpYyBtYXRjaGluZywgZmVhdHVyZSBzZWxlY3Rpb24gYW5kIGh5cGVycGFyYW1ldGVyIHR1bmluZyB3ZXJlIG5vdCBwZXJmb3JtZWQgdXNpbmcgYm9vdHN0cmFwIHNhbXBlcy4gVGhlIG1vZGVscyB3ZXJlIHByZXZpb3VzbHkgdHJhaW5lZCB1c2luZyB0aGUgZnVsbCB0cmFpbmluZyBzYW1wbGUuIFRoZSByZXN1bHRhbnQgbWF0Y2ggYmV0YSBzdWJzZXRzIHdlcmUgc2VyaWFsaXplZCBhbmQgdXBsb2FkZWQgdG8gR0NQLgoKVHJhaW5lZDogYGZlYXR1cmVfc2VsZWN0aW9uX2dlbm1hdGNoX2xpbmVhci5SbWRgCgpgYGB7ciBnZW5tYXRjaCwgZWNobz1GQUxTRSwgZXJyb3I9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmZpbGVfbmFtZSA9ICdmZWF0dXJlX3NlbGVjdGlvbl9nZW5tYXRjaF8uUkRhdGEnCmltYWdlX2ZpbGVfcGF0aCA9IGZpbGUucGF0aCgnZGF0YScsIGZpbGVfbmFtZSkKCiMgUHVsbCBmcm9tIEdDUCBpZiBuZWNlc3NhcnkKaWYgKCFmaWxlLmV4aXN0cyhpbWFnZV9maWxlX3BhdGgpKXsKICBTeXMuc2V0ZW52KCJHQ1NfREVGQVVMVF9CVUNLRVQiID0gIm1vei1meC1kZXYtc3ViYmV0YSIsCiAgICAgICAgICAgIkdDU19BVVRIX0ZJTEUiID0gIm1vei1meC1kZXYtY2Rvd2h5Z2x1bmQtc3ViQmV0YS03ODhmOGYwZDQ2MjcuanNvbiIpCiAgbGlicmFyeShnb29nbGVDbG91ZFN0b3JhZ2VSKQogIGdjc19nZXRfb2JqZWN0KGZpbGUucGF0aCgnZGF0YScsICdtaWxlc3RvbmUyJywgZmlsZV9uYW1lKSwgc2F2ZVRvRGlzayA9IGltYWdlX2ZpbGVfcGF0aCwgb3ZlcndyaXRlID0gVFJVRSkKCn0KbG9hZChpbWFnZV9maWxlX3BhdGgpCmBgYAoKIyMjIEZTIDEKCmBgYHtyIGdlbm1hdGNoX2ZzMSwgZWNobz1GQUxTRX0KZ2VubWF0Y2guZnMxLm1hdGNoZWQgPC0gZGZfdHJhaW5fZ2VuW2ZzMV9yZXN1bHRzJG1hdGNoZXMkaW5kZXguY29udHJvbCxdCmdlbm1hdGNoLmZzMS5xcSA8LSBidWlsZF9xdWFudGlsZV9kZihkZl9yZWxfdmFsLCBnZW5tYXRjaC5mczEubWF0Y2hlZCwgZGZfYmV0YV92YWwpCmdlbm1hdGNoLmZzMS5zY29yZSA8LSBzY29yZV9tb2RlbChidHMsIGdlbm1hdGNoLmZzMS5tYXRjaGVkLCBkZl9yZWxfdmFsKQpnZW5tYXRjaC5mczEuc2NvcmUKYGBgCgpOdW1iZXIgb2YgQmV0YSBzYW1wbGVzOiBgciBucm93KGdlbm1hdGNoLmZzMS5tYXRjaGVkKWAKCmBgYHtyIGdlbm1hdGNoX2ZzMV92YWxfcGx0LCBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD0yNSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0KcGxvdF92YWxpZGF0aW9uX3Jlc3VsdHMoZGZfcmVsX3ZhbCwgZ2VubWF0Y2guZnMxLm1hdGNoZWQsIGRmX2JldGFfdmFsKQpgYGAKCiMjIyMgUG9wLlNpemUgNTAwCgpgYGB7ciBnZW5tYXRjaF9mczFfNTAwLCBlY2hvPUZBTFNFfQpnZW5tYXRjaC5mczEuNTAwLm1hdGNoZWQgPC0gZGZfdHJhaW5fZ2VuW2ZzMV81MDBfcmVzdWx0cyRtYXRjaGVzJGluZGV4LmNvbnRyb2wsXQpnZW5tYXRjaC5mczEuNTAwLnFxIDwtIGJ1aWxkX3F1YW50aWxlX2RmKGRmX3JlbF92YWwsIGdlbm1hdGNoLmZzMS41MDAubWF0Y2hlZCwgZGZfYmV0YV92YWwpCmdlbm1hdGNoLmZzMS41MDAuc2NvcmUgPC0gc2NvcmVfbW9kZWwoYnRzLCBnZW5tYXRjaC5mczEuNTAwLm1hdGNoZWQsIGRmX3JlbF92YWwpCmdlbm1hdGNoLmZzMS41MDAuc2NvcmUKYGBgCgpOdW1iZXIgb2YgQmV0YSBzYW1wbGVzOiBgciBucm93KGdlbm1hdGNoLmZzMS41MDAubWF0Y2hlZClgCgpgYGB7ciBnZW5tYXRjaF9mczFfNTAwX3ZhbF9wbHQsIGZpZy53aWR0aD0xNSxmaWcuaGVpZ2h0PTI1LCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlY2hvPUZBTFNFfQpwbG90X3ZhbGlkYXRpb25fcmVzdWx0cyhkZl9yZWxfdmFsLCBnZW5tYXRjaC5mczEuNTAwLm1hdGNoZWQsIGRmX2JldGFfdmFsKQpgYGAKCgojIyMgRlMgMwoKYGBge3IgZ2VubWF0Y2hfZnMzLCBlY2hvPUZBTFNFfQpnZW5tYXRjaC5mczMubWF0Y2hlZCA8LSBkZl90cmFpbl9nZW5bZnMzX3Jlc3VsdHMkbWF0Y2hlcyRpbmRleC5jb250cm9sLF0KZ2VubWF0Y2guZnMzLnFxIDwtIGJ1aWxkX3F1YW50aWxlX2RmKGRmX3JlbF92YWwsIGdlbm1hdGNoLmZzMy5tYXRjaGVkLCBkZl9iZXRhX3ZhbCkKZ2VubWF0Y2guZnMzLnNjb3JlIDwtIHNjb3JlX21vZGVsKGJ0cywgZ2VubWF0Y2guZnMzLm1hdGNoZWQsIGRmX3JlbF92YWwpCmdlbm1hdGNoLmZzMy5zY29yZQpgYGAKCk51bWJlciBvZiBCZXRhIHNhbXBsZXM6IGByIG5yb3coZ2VubWF0Y2guZnMzLm1hdGNoZWQpYAoKYGBge3IgZ2VubWF0Y2hfZnMzX3ZhbF9wbHQsIGZpZy53aWR0aD0xNSxmaWcuaGVpZ2h0PTI1LCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlY2hvPUZBTFNFfQpwbG90X3ZhbGlkYXRpb25fcmVzdWx0cyhkZl9yZWxfdmFsLCBnZW5tYXRjaC5mczMubWF0Y2hlZCwgZGZfYmV0YV92YWwpCmBgYAoKIyMjIEZTIDUKCmBgYHtyIGdlbm1hdGNoX2ZzNSwgZWNobz1GQUxTRX0KZ2VubWF0Y2guZnM1Lm1hdGNoZWQgPC0gZGZfdHJhaW5fZ2VuW2ZzNV9yZXN1bHRzJG1hdGNoZXMkaW5kZXguY29udHJvbCxdCmdlbm1hdGNoLmZzNS5xcSA8LSBidWlsZF9xdWFudGlsZV9kZihkZl9yZWxfdmFsLCBnZW5tYXRjaC5mczUubWF0Y2hlZCwgZGZfYmV0YV92YWwpCmdlbm1hdGNoLmZzNS5zY29yZSA8LSBzY29yZV9tb2RlbChidHMsIGdlbm1hdGNoLmZzNS5tYXRjaGVkLCBkZl9yZWxfdmFsKQpnZW5tYXRjaC5mczUuc2NvcmUKYGBgCgpOdW1iZXIgb2YgQmV0YSBzYW1wbGVzOiBgciBucm93KGdlbm1hdGNoLmZzMy5tYXRjaGVkKWAKCmBgYHtyIGdlbm1hdGNoX2ZzNV92YWxfcGx0LCBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD0yNSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0KcGxvdF92YWxpZGF0aW9uX3Jlc3VsdHMoZGZfcmVsX3ZhbCwgZ2VubWF0Y2guZnM1Lm1hdGNoZWQsIGRmX2JldGFfdmFsKQpgYGAKCiMgUmVzdWx0cwoKR2VuZXRpYyBtYXRjaGluZyBwcm9kdWNlcyB0aGUgb3B0aW1hbCBtYXRjaGluZyByZXN1bHRzLiBJdCB5aWVsZHMgdGhlIGxvd2VzdCBzY29yZXMsIHdpdGhvdXQgZHJvcHBpbmcgbGFyZ2Ugc2FtcGxlIG9mIHRoZSBCZXRhIHByb2ZpbGVzLiBVbnN1cnByaXNpbmdseSwgbWF0Y2hpbmcgZGlyZWN0bHksIGFuZCBvbmx5LCBvbiB0aGUgcGVyZm9ybWFuY2UgbWV0cmljcyB5aWVsZHMgdGhlIGJlc3QgcmVzdWx0cy4gSW5jcmVhc2luZyB0aGUgcG9wdWxhdGlvbiBzaXplIGh5cGVycGFyYW1ldGVyIHNsaWdodGx5IGRlY3JlYXNlZCBtb2RlbCBwZXJmb3JtYW5jZSwgd2hpY2ggaXMgc3VycHJpc2luZy4gQ29uc2lkZXJpbmcgdGhpcyBzdWJzdGFudGlhbGx5IGluY3JlYXNlcyB0cmFpbmluZyB0aW1lcywgdGhlIGxvd2VyIHZhbHVlIHdpbGwgYmUgdXNlZC4gCgoqKkNvbmNsdXNpb24qKjogT3B0aW1hbCBtb2RlbCBmcmFtZXdvcmsKCiogbWF0Y2hpbmcgYWxnb3JpdGhtOiBHZW5ldGljIE1hdGNoaW5nCiogZmVhdHVyZXM6IE9ubHkgcGVyZm9ybWFuY2UgbWV0cmljcwoqIGh5cGVycGFyYW1ldGVyczogRGVmYXVsdCBmb3IgYE1hdGNoaW5nYCBsaWJyYXJ5CgojIFB1c2ggdG8gR0NQCgpUaGUgYm9vc3RyYXAgcmVwbGljYXRlcyBmb3IgdmFsaWRhdGlvbiwgb3ZlcnNhbXBsZWQgdHJhaW5pbmlnIGRhdGFzZXRzLCByZXN1bHRhbnQgcXVhbnRpbGUgY2FsY3VsYXRpb25zLCBhbmQgbWF0Y2hlZCBkYXRhc2V0cyBhcmUgc2F2ZWQgdG8gYW4gUiBpbWFnZS4gVGhlbiB1cGxvYWRlZCB0byB0aGUgcHJvamVjdCBHQ1AgYnVja2V0LgoKYGBge3Igc2VyaWFsaXplX2djcCwgZWNobz1GQUxTRX0KcmVzdWx0c19maWxlX25hbWUgPSAndmFsaWRhdGlvbl9yZXN1bHRzXzIwMTkxMTA2LlJEYXRhJwpyZXN1bHRzX2ZpbGVfcGF0aCA9IGZpbGUucGF0aCgnZGF0YScsIHJlc3VsdHNfZmlsZV9uYW1lKQoKb2JqZWN0cyA8LSBjKAogIGxzKHBhdHRlcm4gPSAnZ2VubWF0Y2gnKSwKICBscyhwYXR0ZXJuID0gJ25uJyksCiAgbHMocGF0dGVybiA9ICdeYnRzJCcpLAogIGxzKHBhdHRlcm4gPSAnXmRmcyQnKQopCgpzYXZlKGxpc3QgPSBvYmplY3RzLCBmaWxlID0gcmVzdWx0c19maWxlX3BhdGgpCmBgYAoKCmBgYHtyIHB1c2hfdG9fZ2NwLCBlY2hvPUZBTFNFfQpnY3NfZmlsZV9wYXRoIDwtIGZpbGUucGF0aCgnZGF0YScsICdtaWxlc3RvbmUyJywgcmVzdWx0c19maWxlX25hbWUpCgpTeXMuc2V0ZW52KCJHQ1NfREVGQVVMVF9CVUNLRVQiID0gIm1vei1meC1kZXYtc3ViYmV0YSIsCiAgICAgICAgICAgIkdDU19BVVRIX0ZJTEUiID0gIm1vei1meC1kZXYtY2Rvd2h5Z2x1bmQtc3ViQmV0YS03ODhmOGYwZDQ2MjcuanNvbiIpCgpsaWJyYXJ5KGdvb2dsZUNsb3VkU3RvcmFnZVIpCgpwcm9qX2ZpbGVzID0gZ2NzX2xpc3Rfb2JqZWN0cygpCgppZiAoZ2NzX2ZpbGVfcGF0aCAlaW4lIHByb2pfZmlsZXMkbmFtZSkgewogIHByaW50KHBhc3RlKCdQcmV2aW91c2x5IHRyYWluZWQgcmVzdWx0cyBhbHJlYWR5IGV4aXN0OicsIGdjc19maWxlX3BhdGgpKQp9IGVsc2UgewogIHByaW50KHBhc3RlKCdVcGxvYWRpbmcgdmFsaWRhdGlvbiByZXN1bHRzIHRvIEdDUDonLCBnY3NfZmlsZV9wYXRoKSkKICB1cGxvYWRfdHJ5IDwtIGdjc191cGxvYWQocmVzdWx0c19maWxlX3BhdGgsIG5hbWUgPSBnY3NfZmlsZV9wYXRoKQogIHVwbG9hZF90cnkKfQpgYGAKCgoK